package com.guan.study.utils;

import com.alibaba.excel.EasyExcel;
import lombok.extern.log4j.Log4j2;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * @Date: 2023/6/26 10:55
 * @Author: yaobai
 * @Description: excel导入导出, 都是单个sheet操作
 */
@Log4j2
public class ExcelUtil {

    //protected static final JsonBinder JSON_BINDER = JsonBinder.buildNormalBinder();

    /**
     * @date: 2023/8/11 8:21
     * @author: yaobai
     * @description: web中简单的导出。
     * @param: [response 响应对象, fname 文件名, sheetName 工作簿名称,
     *              dataList 导出数据, clazz 数据类型]
     * @return:
     **/
    public static <T> void exportExcel(HttpServletResponse response,
                                       String fname,
                                       String sheetName,
                                       List<T> dataList,
                                       Class<T> clazz ) throws Exception {

        //log.info("ExcelUtil ==> exportExcel-1 导出数据：dataList = {}", JSON_BINDER.toJson(dataList));
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode(fname, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        EasyExcel.write(response.getOutputStream(), clazz)
                .sheet(sheetName)
                .doWrite(dataList);

    }


    /**
     * @date: 2023/8/11 8:21
     * @author: yaobai
     * @description: web中的导出。在数据主体部分，对指定列按规则合并行，规则：相邻行的数据一致就合并。
     * @param: [response 响应对象, fname 文件名, sheetName 工作簿名称,
 *              dataList 导出数据, mergeList 合并列, clazz 数据类型]
     * @return:
     **/
    public static <T> void exportExcel(HttpServletResponse response,
                                          String fname,
                                          String sheetName,
                                          List<T> dataList,
                                          List<String> mergeList,
                                          Class<T> clazz ) throws Exception {

        //log.info("ExcelUtil ==> exportExcel-2 导出数据：dataList = {}", JSON_BINDER.toJson(dataList));
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode(fname, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        EasyExcel.write(response.getOutputStream(), clazz)
                .registerWriteHandler(new CustomMergeColumn(mergeList))
                .sheet(sheetName)
                .doWrite(dataList);

    }

    /**
     * @date: 2023/8/11 12:32
     * @author: yaobai
     * @description:  导出时，设置某一行的行高。
     * 例如：导出包括4列数据的excel，表头是2行，第1行表头是合并4列，第2行表头单独的4列。
     * 常规情况，在excel实体类中没有第1行表头的属性，而在类上加注解 @HeadHeight()
     * 会同时设置两行表头的行高，无法单独设置第1行表头的高。
     * 所以，可以使用该方法多传两个参数即可单独设置。
     * @param: [response 响应对象, fname 文件名, sheetName 工作簿名,
     *          dataList 导出数据, mergeList 合并列, rowIndex 行索引, rowHeight 行高, clazz 数据类型]
     * @return:
     **/
    public static <T> void exportExcel(HttpServletResponse response,
                                       String fname,
                                       String sheetName,
                                       List<T> dataList,
                                       List<String> mergeList,
                                       Integer rowIndex,
                                       Integer rowHeight,
                                       Class<T> clazz ) throws Exception {

        //log.info("ExcelUtil ==> exportExcel-3 导出数据：dataList = {}", JSON_BINDER.toJson(dataList));
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
        String fileName = URLEncoder.encode(fname, "UTF-8").replaceAll("\\+", "%20");
        response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");

        if ( rowIndex == null || rowHeight == null ){
            throw new Exception("行索引和行高不能为空");
        }

        CustomWriteHandler customWriteHandler = new CustomWriteHandler();
        customWriteHandler.setRowIndex(rowIndex);
        customWriteHandler.setRowHeight(rowHeight);

        EasyExcel.write(response.getOutputStream(), clazz)
                .registerWriteHandler(new CustomMergeColumn(mergeList))
                .registerWriteHandler(customWriteHandler)
                .sheet(sheetName)
                .doWrite(dataList);

    }

    /**
     * @date: 2023/6/30 15:50
     * @author: yaobai
     * @description: 简单的导出excel，
     * 1.会在文件地址中保存当前导出的文件，时间长了会占用内存资源，需要配合定时任务定期清理文件
     * 2.realpath 导出路径哪里来？
     *   谁调用谁提供，在调用者所在类中使用
     *      @Value("${project.realpath}")
     *      private String realpath;
     *   从配置文件中获取值，对应模块的配置文件也应该声明改配置信息
     * @param: [realpath 导出路径，fname 导出文件名, sheetName sheet名, dataList 导出数据, clazz 数据类型]
     * @return:
     **/
    public static <T> String exportExcel(String realpath,
                                         String fname,
                                         String sheetName,
                                         List<T> dataList,
                                         Class<T> clazz ){
        log.info("下载路径：{}",realpath);
        //log.info("ExcelUtil ==> exportExcel-4 导出数据：dataList = {}", JSON_BINDER.toJson(dataList));
        File folder = new File(realpath);
        if (!folder.isDirectory()){
            folder.mkdirs();
        }
        String fileName = realpath  + fname  + ".xlsx";

        EasyExcel.write(fileName, clazz)
                .sheet(sheetName)
                .doWrite(dataList);

        return fileName;
    }

    /**
     * @date: 2023/7/3 14:08
     * @author: yaobai
     * @description: 单个sheet的excel导入，为了方便数据校验和友好提示，返回数据集合、表头集合和行号集合。
     * @param: [multipartFile, clazz]
     * @return:
     **/
    public static<T> Map<String,Object> importExcel(MultipartFile multipartFile,
                                                    Class<T> clazz) {
        log.info("ExcelUtil ==> importExcel 开始导入数据.... ");
        //数据集合
        List<T> list = new ArrayList<>();
        //表头集合
        Map<Integer, String> tableHeadMap = new HashMap<>();
        //行号集合
        List<Integer> rowNoList = new ArrayList<>();
        //返回的数据集合
        Map<String, Object> map = new HashMap<>();
        try {
            ReadLisitener<T> readLisitener = new ReadLisitener<>();
            EasyExcel.read(multipartFile.getInputStream(), clazz, readLisitener)
                    .sheet(0)
                    .doRead();
            list = readLisitener.list;
            tableHeadMap = readLisitener.tableHeadMap;
            rowNoList = readLisitener.rowNoList;

        } catch (Exception e) {
            e.printStackTrace();
        }

        map.put("head",tableHeadMap);
        map.put("content",list);
        map.put("rowNoList",rowNoList);
        return map;
    }
}
